package spring.boot.client.client;

import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import spring.boot.client.codec.Invocation;
import spring.boot.client.common.NettyClientConstant;
import spring.boot.client.message.heartbeat.HeartbeatRequest;

@Slf4j
@Component
@ChannelHandler.Sharable
public class NettyClientHandler extends ChannelInboundHandlerAdapter {

    @Autowired
    private NettyClient nettyClient;

    /**
     * 服务端断开，会触发此方法
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.info("Netty服务端[{}]已经断开,开始发起重连......", ctx.channel().id());
        nettyClient.reconnect();
        // 继续触发事件
        super.channelInactive(ctx);
    }

    /**
     * 服务端连接发生异常时，调用此方法
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) {
        log.error("Netty服务端连接[{}]发生异常,errorMsg=[{}]", ctx.channel().id(), e.getMessage(), e);
        // 断开连接
        ctx.channel().close();
    }

    /**
     * 超过指定空闲时间，会调用此方法，向服务端发起一次心跳
     * IdleStateHandler
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state() == IdleState.WRITER_IDLE) {
                HeartbeatRequest heartbeatRequest = new HeartbeatRequest(NettyClientConstant.HEART_START_NUM);
                Invocation invocation = new Invocation(HeartbeatRequest.TYPE, heartbeatRequest);
                log.debug("客户端向服务端[{}]发送心跳,num=[{}]", ctx.channel().id(), NettyClientConstant.HEART_START_NUM);
                ctx.writeAndFlush(invocation).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
                NettyClientConstant.HEART_START_NUM++;
            }
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }

}
